package msgo

import (
	"fmt"
	"log"
	"net/http"
)

const (
	Any = "any"
)

type MiddlewareFunc func(handlerFunc HandlerFunc) HandlerFunc

type HandlerFunc func(ctx *Context)

type Context struct {
	W http.ResponseWriter
	R *http.Request
}

type router struct {
	routerGroups []*routerGroup
}

type routerGroup struct {
	name               string
	handlerFuncMap     map[string]map[string]HandlerFunc
	middlewaresFuncMap map[string]map[string][]MiddlewareFunc
	treeNode           *treeNode
	middlewares        []MiddlewareFunc
}

type Engine struct {
	*router
}

func New() *Engine {
	return &Engine{
		&router{},
	}
}
func (r *routerGroup) PreHandle(middlewares ...MiddlewareFunc) {
	r.middlewares = append(r.middlewares, middlewares...)
}

func (r *routerGroup) methodHandle(name, method string, h HandlerFunc, ctx *Context) {
	for _, middlewareFunc := range r.middlewares {
		h = middlewareFunc(h)
	}
	for _, middlewareFunc := range r.middlewaresFuncMap[name][method] {
		h = middlewareFunc(h)
	}
	h(ctx)
}

// 注册隐射关系
func (r *routerGroup) handle(name, method string, handlerFunc HandlerFunc, middlewareFunc ...MiddlewareFunc) {
	if _, ok := r.handlerFuncMap[name]; !ok {
		r.handlerFuncMap[name] = make(map[string]HandlerFunc)
		r.middlewaresFuncMap[name] = make(map[string][]MiddlewareFunc)
	}
	if _, ok := r.handlerFuncMap[name][method]; ok {
		panic("有重复的路由了")
	}
	r.handlerFuncMap[name][method] = handlerFunc
	r.middlewaresFuncMap[name][method] = append(r.middlewaresFuncMap[name][method], middlewareFunc...)
	r.treeNode.Put(name)
}
func (r *routerGroup) Any(name string, handlerFunc HandlerFunc, middlewareFunc ...MiddlewareFunc) {
	r.handle(name, Any, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Get(name string, handlerFunc HandlerFunc, middlewareFunc ...MiddlewareFunc) {
	r.handle(name, http.MethodGet, handlerFunc, middlewareFunc...)
}

func (r *routerGroup) Post(name string, handlerFunc HandlerFunc, middlewareFunc ...MiddlewareFunc) {
	r.handle(name, http.MethodPost, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Delete(name string, handlerFunc HandlerFunc, middlewareFunc ...MiddlewareFunc) {
	r.handle(name, http.MethodDelete, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Put(name string, handlerFunc HandlerFunc, middlewareFunc ...MiddlewareFunc) {
	r.handle(name, http.MethodPut, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Patch(name string, handlerFunc HandlerFunc, middlewareFunc ...MiddlewareFunc) {
	r.handle(name, http.MethodPatch, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Options(name string, handlerFunc HandlerFunc, middlewareFunc ...MiddlewareFunc) {
	r.handle(name, http.MethodOptions, handlerFunc, middlewareFunc...)
}
func (r *routerGroup) Head(name string, handlerFunc HandlerFunc, middlewareFunc ...MiddlewareFunc) {
	r.handle(name, http.MethodHead, handlerFunc, middlewareFunc...)
}

func (r *router) Group(name string) *routerGroup {
	g := &routerGroup{
		name:               name,
		handlerFuncMap:     make(map[string]map[string]HandlerFunc),
		middlewaresFuncMap: make(map[string]map[string][]MiddlewareFunc),
		treeNode:           &treeNode{name: "/", children: make([]*treeNode, 0)},
		middlewares:        []MiddlewareFunc{},
	}
	r.routerGroups = append(r.routerGroups, g)
	return g
}

func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	routerGroups := e.router.routerGroups
	method := r.Method
	for _, g := range routerGroups {
		routerName := SubStringLast(r.RequestURI, "/"+g.name)
		node := g.treeNode.Get(routerName)
		if node != nil {
			//路由匹配上了
			ctx := &Context{
				W: w,
				R: r,
			}
			if methodHandle, ok := g.handlerFuncMap[node.routerName]; ok {
				if handle, ok1 := methodHandle[Any]; ok1 {
					g.methodHandle(node.routerName, Any, handle, ctx)
					return
				}
				if handle, ok1 := methodHandle[r.Method]; ok1 {
					g.methodHandle(node.routerName, r.Method, handle, ctx)
					return
				}
				w.WriteHeader(http.StatusMethodNotAllowed)
				fmt.Fprintf(w, "%s %s not allowed \n", r.RequestURI, method)
				return
			}
		}
	}
	w.WriteHeader(http.StatusNotFound)
	fmt.Fprintln(w, r.RequestURI+" not found")
	return
}

func (e *Engine) Run() {
	err := http.ListenAndServe(":8111", e)
	if err != nil {
		log.Fatal(err)
	}
}
